#pragma once

#include "zen_class.h"
#include "zen_game_node.h"
#include <vector>
#include <deque>
#include <functional>

namespace Zen
{
	namespace Game
	{

		class Action;

		struct ActionRuntimeStatus
		{
			std::shared_ptr<Action> action{};
			std::weak_ptr<Node> target{};
			double next_time{};
			double time{};
			bool valid = true;
		};

		/// <summary>
		/// Get default ActionDispatcher from Activity.
		/// </summary>
		class ActionDispatcher
		{
		public:
			void OnUpdate(float interval);

		public: // action.
			/**
			 @return successed?
			 */
			bool RunAction(std::shared_ptr<Action> action, std::shared_ptr<Node> target);
			/**
			 @function StopAction
			 @return count of effected
			 */
			int StopAction(std::shared_ptr<Action> action);

			/**
			 @return count of effected.
			 */
			int StopActionsOnNode(std::shared_ptr<Node> node);

			void StopAllActions();

		protected:
			void PushRuntimeQueue_(std::shared_ptr<ActionRuntimeStatus> item);
			std::deque<std::shared_ptr<ActionRuntimeStatus>> mActionsRunning;
			std::vector<std::shared_ptr<ActionRuntimeStatus>> mActionsNew;
			double mRunningSeconds = 0;
		};
		/*!
		 @class Action
		 @abstract The base class of Action.
		 */
		class Action : public Object
		{
		public:
			/*!
			 @function start
			 @abstract This was called (once) when the action started. [to override]
			 @return The Delay time before Run. <br>
			 if >= 0, Delay for Run, if the value was less than Update interval, it will Run in next Update. <br>
			 if < 0, action stop.
			 */
			virtual float Start()
			{
				return mDelay;
			}

			/*!
			 @function Run
			 @abstract This was called in time.
			 @return The interval for next Run. see ref function start.
			 */
			virtual float Run(float)
			{
				return -1;
			}

			ZEN_GET_SET_COPY(float, mDelay, GetDelay, SetDelay){};
			ZEN_GET_SET_COPY(float, mInterval, GetInterval, SetInterval){};
		};

		/*!
		 @class ActionTimed
		 @abstract The action that has a time life. <br>
		 Basicly, this kind of action has age/lifetime, and it will stop after runout lifetime.
		 */
		class ActionTimed : public Action
		{
		public:
			virtual float Start() override;

			virtual float Run(float) override;

			ZEN_GET_SET_COPY(float, mLifeTime, GetLifetime, SetLifetime){};
			ZEN_GET_COPY(float, mAge, GetAge){};
		};

		/*!
		 @class ActionCall
		 @abstract Action to call a function times.
		 */
		class ActionCall : public Action
		{
		public:
			// n = [0, times)
			using Function = std::function<bool(float interval, int n)>;

		protected:
			ZEN_GET_SET_COPY(Function, mCall, GetCallFunc, SetCallFunc){};
			ZEN_GET_COPY(int, mRunTimes, GetRunTimes) = 0;
			ZEN_GET_SET_COPY(int, mTimes, GetRepeatTimes, SetRepeatTimes) = 0;

		public:
			static std::shared_ptr<Action> Generate(Function call, int times, float Delay = 0.f, float interval = 0.f);

		public:
			/*!
			 @function Init
			 @abstract set function and repeat times.
			 @param call the Function to call. if the Function returns (<0), the action will stop.
			 @param times if <=0, this will repeat forever; <br> if ==+N repeat N times.
			 */
			void Init(Function call, int times);

			virtual float Start() override;

			virtual float Run(float) override;
		};

		/*!
		 @class ActionRepeat
		 @abstract shell an action, and Run that action time and again.
		 */
		class ActionRepeat : public Action
		{
		protected:
			ZEN_GET_SET_COPY(std::shared_ptr<Action>, mAction, GetTargetAction, SetTargetAction){};
			ZEN_GET_COPY(int, mRunTimes, GetRunTimes){};
			ZEN_GET_SET_COPY(int, mTimes, GetRepeatTimes, SetRepeatTimes){};
			bool mIsInnerRun = false;

		public:
			static std::shared_ptr<Action> Generate(std::shared_ptr<Action>, int times, float Delay = 0.f, float interval = 0.f);

		public:
			/*!
			 @function Init
			 @abstract set action and repeat times.
			 @param action repeated target action.
			 @param times repeat times. <br>
			 if times <= 0 repeat forever.
			 */
			void Init(std::shared_ptr<Action> action, int times);

			virtual float Run(float) override;

			virtual float Start() override;
		};
		/*!
		 @class ActionSequence
		 @abstract Run a sequence of actions.
		 */
		class ActionSequence : public Action
		{
		protected:
			std::vector<std::shared_ptr<Action>> mActions;
			int mIndex = 0;
			bool mIsInnerRun = false;

		public:
			static std::shared_ptr<Action> Generate(std::vector<std::shared_ptr<Action>>, float Delay = 0.f, float interval = 0.f);

		public:
			void InitActions(std::vector<std::shared_ptr<Action>>);

			void AddAction(std::shared_ptr<Action>);

			virtual float Run(float) override;

			virtual float Start() override;
		};
	} // namespace Game
} // namespace Zen
